﻿using System;
using System.Collections.Generic;
using System.Text;
using DevExpress.ExpressApp.DC;
using DevExpress.Persistent.Base;
using DevExpress.Xpo;
using DevExpress.Persistent.Validation;
using DevExpress.ExpressApp;
using DevExpress.Data.Filtering;
using System.Security;
using System.Collections.ObjectModel;
using DevExpress.ExpressApp.Editors;
using System.Drawing;
using DevExpress.Persistent.Base.Security;
using System.ComponentModel;
using DevExpress.ExpressApp.Security;
using DevExpress.Xpo.Metadata;

namespace DomainComponents.Common {
    [DomainComponent]
    [XafDefaultProperty("DisplayName")]
    [VisibleInReports(false)]
    [ImageName("BO_User")]
    public interface IDCUser {
        string DisplayName { get; }
        string UserName { get; set; }
    }

    [DomainLogic(typeof(IDCUser))]
    public class DCUserLogic {
        public static string Get_DisplayName(IDCUser user) {
            return user.UserName;
        }
    }

    [DomainComponent]
    [ImageName("BO_User")]
    public interface IPersistentAuthenticationStandardUser : DevExpress.Persistent.Base.Security.IAuthenticationStandardUser, IDCUser {
        new bool ChangePasswordOnFirstLogon { get; set; }

        [System.ComponentModel.Browsable(false)]
        string StoredPassword { get; set; }
    }

    [DomainComponent]
    public interface IPersistentAuthenticationActiveDirectoryUser : IDCUser, DevExpress.Persistent.Base.Security.IAuthenticationActiveDirectoryUser {
    }

    [DomainComponent]
    [ImageName("BO_User")]
    public interface IPersistentSimpleSecurityUser : IDCUser, DevExpress.Persistent.Base.Security.ISimpleUser {
        [System.ComponentModel.DefaultValue(true)]
        new bool IsActive { get; set; }
        new bool IsAdministrator { get; set; }
    }

    [DomainComponent]
    [ImageName("BO_User")]
    public interface IPersistentComplexSecurityUser : DevExpress.Persistent.Base.Security.IUserWithRoles, IDCUser {
        [System.ComponentModel.DefaultValue(true)]
        new bool IsActive { get; set; }
        new IList<IPermission> Permissions { get; }
        [BackReferenceProperty("PersistentUsers")]
        IList<IPersistentRole> PersistentRoles { get; }
        [System.ComponentModel.Browsable(false)]
        new IList<DevExpress.Persistent.Base.Security.IRole> Roles { get; }
    }

    public class IPermissionToStringConverter : DevExpress.Xpo.Metadata.ValueConverter {
        public override object ConvertToStorageType(object value) {
            if(value != null && !(value is IPermission)) {
                throw new ArgumentException(value.GetType().FullName);
            }
            if(value == null) {
                return null;
            }
            return ((IPermission)value).ToXml().ToString();
        }
        public override object ConvertFromStorageType(object value) {
            if(value != null && !(value is string)) {
                throw new ArgumentException(value.GetType().FullName);
            }
            if(value == null || string.IsNullOrEmpty((string)value)) {
                return null;
            }
            try {
                SecurityElement securityElement = SecurityElement.FromString((string)value);
                string typeName = securityElement.Attribute("class");
                string assemblyName = securityElement.Attribute("assembly");
                IPermission result = (IPermission)ReflectionHelper.CreateObject(typeName);
                result.FromXml(securityElement);
                return result;
            }
            catch(Exception e) {
                Tracing.Tracer.LogError(e);
            }
            return null;
        }
        public override Type StorageType {
            get { return typeof(string); }
        }
    }

    public class TypeToStringConverter : ValueConverter {
        public override object ConvertFromStorageType(object stringObjectType) {
            return ReflectionHelper.FindType((string)stringObjectType);
        }
        public override object ConvertToStorageType(object objectType) {
            if(objectType == null) {
                return null;
            }
            return ((Type)objectType).FullName;
        }
        public override Type StorageType {
            get { return typeof(string); }
        }
    }

    [DomainComponent]
    public interface IObjectAccessPermission {
        [ValueConverter(typeof(TypeToStringConverter))]
        [Size(512)]
        [TypeConverter(typeof(PermissionTargetBusinessClassListConverter))]
        Type TargetObjectType { get; set; }

        ObjectAccessModifier CreateAccess { get; set; }
        ObjectAccessModifier DeleteAccess { get; set; }
        ObjectAccessModifier ReadAccess { get; set; }
        ObjectAccessModifier WriteAccess { get; set; }
        ObjectAccessModifier NavigateAccess { get; set; }
    }

    [DomainComponent]
    [VisibleInReports(false)]
    [ImageName("BO_Role")]
    public interface IPersistentRole : DevExpress.Persistent.Base.Security.IRole {
        new string Name { get; set; }
        bool CanEditModel { get; set; }
        [BackReferenceProperty("PersistentRoles")]
        IList<IPersistentComplexSecurityUser> PersistentUsers { get; }
        [System.ComponentModel.Browsable(false)]
        new IList<DevExpress.Persistent.Base.Security.IUser> Users { get; }
        [DevExpress.ExpressApp.DC.Aggregated]
        IList<IObjectAccessPermission> ObjectAccessPermissions { get; }
        [System.ComponentModel.Browsable(false)]
        new ReadOnlyCollection<IPermission> Permissions { get; }
        void AddPermission(Type objectType, ObjectAccess access);
        [CreateInstance]
        IObjectAccessPermission CreateObjectAccessPermission();
    }

    [DomainLogic(typeof(IPersistentAuthenticationStandardUser))]
    public class PersistentAuthenticationStandardUserUserLogic {
        public static bool ComparePassword(IPersistentAuthenticationStandardUser user, string password) {
            return UserImpl.ComparePassword(user.StoredPassword, password);
        }

        public static void SetPassword(IPersistentAuthenticationStandardUser user, string password) {
            user.StoredPassword = UserImpl.GeneratePassword(password);
        }
    }

    [DomainLogic(typeof(IPersistentComplexSecurityUser))]
    public class PersistentComplexSecurityUserLogic {
        public static void ReloadPermissions(IPersistentComplexSecurityUser user, IObjectSpace os) {
            os.ReloadCollection(user.PersistentRoles);
            foreach(IPersistentRole role in user.PersistentRoles) {
                os.ReloadObject(role);
                os.ReloadCollection(role.ObjectAccessPermissions);
                foreach(IObjectAccessPermission permission in role.ObjectAccessPermissions) {
                    os.ReloadObject(permission);
                }
            }
        }
        public static IList<IPermission> Get_Permissions(IPersistentComplexSecurityUser user) {
            return UserImpl.GetUserPermissions(user);
        }
        public static IList<DevExpress.Persistent.Base.Security.IRole> Get_Roles(IPersistentComplexSecurityUser user) {
            return new ListConverter<DevExpress.Persistent.Base.Security.IRole, IPersistentRole>(user.PersistentRoles);
        }
    }

    [DomainLogic(typeof(IPersistentRole))]
    public class PersistentRoleLogic {
        public static void AddPermission(IPersistentRole role, Type objectType, ObjectAccess access) {
            IObjectAccessPermission permission = role.CreateObjectAccessPermission();
            permission.TargetObjectType = objectType;
            if((access & ObjectAccess.Read) == ObjectAccess.Read) {
                permission.ReadAccess = ObjectAccessModifier.Allow;
            }
            if((access & ObjectAccess.Write) == ObjectAccess.Write) {
                permission.WriteAccess = ObjectAccessModifier.Allow;
            }
            if((access & ObjectAccess.Create) == ObjectAccess.Create) {
                permission.CreateAccess = ObjectAccessModifier.Allow;
            }
            if((access & ObjectAccess.Delete) == ObjectAccess.Delete) {
                permission.DeleteAccess = ObjectAccessModifier.Allow;
            }
            if((access & ObjectAccess.Navigate) == ObjectAccess.Navigate) {
                permission.NavigateAccess = ObjectAccessModifier.Allow;
            }
            role.ObjectAccessPermissions.Add(permission);
        }
        public static IList<DevExpress.Persistent.Base.Security.IUser> Get_Users(IPersistentRole role) {
            return new ListConverter<DevExpress.Persistent.Base.Security.IUser, IPersistentComplexSecurityUser>(role.PersistentUsers);
        }
        public static ReadOnlyCollection<IPermission> Get_Permissions(IPersistentRole role) {
#if DebugTest
            List<IPermission> value;
            if(DEBUG_Permissions.TryGetValue(role, out value))
                return DEBUG_Permissions[role].AsReadOnly();
#endif
            List<IPermission> permissions = new List<IPermission>();
            EditModelPermission canEditModel = new EditModelPermission();
            canEditModel.Modifier = role.CanEditModel ? ModelAccessModifier.Allow : ModelAccessModifier.Deny;
            permissions.Add(canEditModel);

            foreach(IObjectAccessPermission persistentPermission in role.ObjectAccessPermissions) {
                ObjectAccessPermission permission = new ObjectAccessPermission();
                permission.ObjectType = persistentPermission.TargetObjectType;
                permission.CreateAccess = persistentPermission.CreateAccess;
                permission.ReadAccess = persistentPermission.ReadAccess;
                permission.WriteAccess = persistentPermission.WriteAccess;
                permission.NavigateAccess = persistentPermission.NavigateAccess;
                permission.DeleteAccess = persistentPermission.DeleteAccess;
                permissions.Add(permission);
            }
            return permissions.AsReadOnly();
        }
#if DebugTest
        public static Dictionary<IPersistentRole, List<IPermission>> DEBUG_Permissions = new Dictionary<IPersistentRole,List<IPermission>>();
#endif
    }
}

